﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

using LowLevelGraphics.ColorSpaces;

namespace LowLevelGraphics
{
    /// <summary>
    /// Create Color Gradient
    /// </summary>
    public class ColorList
    {
        protected static Dictionary<Color, Color> m_aPseudoColorList = new Dictionary<Color, Color>();

        /// <summary>
        /// 
        /// </summary>
        /// <param name="_colorStart"></param>
        /// <param name="_colorEnd"></param>
        public ColorList(int _nSteps, Color _colorStart, Color _colorEnd)
            : this(255, _colorStart.R, _colorStart.G, _colorStart.B, _colorEnd.R, _colorEnd.G, _colorEnd.B)
        {
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="nR1"></param>
        /// <param name="nG1"></param>
        /// <param name="nB1"></param>
        /// <param name="nR2"></param>
        /// <param name="nG2"></param>
        /// <param name="nB2"></param>
        public ColorList(int nSteps, int nR1, int nG1, int nB1, int nR2, int nG2, int nB2)
        {
            float nRInc = (nR1 - nR2) / nSteps;
            float nGInc = (nG1 - nG2) / nSteps;
            float nBInc = (nB1 - nB2) / nSteps;
            int i = 0;

            for (i = 0; i <= nSteps; i++)
            {
                m_aPseudoColorList.Add(Color.FromArgb(i, i, i), Color.FromArgb(i, 255 - i, i));
            }
        }

        /// <summary>
        /// Brighten or darken colormap
        /// </summary>
        public void Brighten(int _nR, int _nG, int _nB, bool _bClipOrClamp)
        {
            foreach (KeyValuePair<Color, Color> keyValuePair in m_aPseudoColorList)
            {
                Color color1 = keyValuePair.Key;
                Color color2 = keyValuePair.Value;

                int nR = color2.R;
                int nG = color2.G;
                int nB = color2.B;

                nR += _nR;
                nG += _nG;
                nB += _nB;

                if (_bClipOrClamp)
                {
                    nR = ImageMath.Clamp(nR, 0, 255);
                    nG = ImageMath.Clamp(nG, 0, 255);
                    nB = ImageMath.Clamp(nB, 0, 255);
                }
                else
                {
                    nR = nR % 255;
                    nG = nG % 255;
                    nB = nB % 255;
                }
            }
        }

        /// <summary>
        /// Rearrange colors in colormap
        /// Description
        /// [Y,newmap] = cmpermute(X,map) randomly reorders the colors in map to produce a new colormap, newmap.
        /// The cmpermute function also modifies the values in X to maintain correspondence between the indices and the colormap, and returns the result in Y. The image Y and associated colormap, newmap, produce the same image as X and map.
        /// [Y,newmap] = cmpermute(X,map,index) uses an ordering matrix (such as the second output of sort) to define the order of colors in the new colormap.
        /// Class Support
        /// </summary>
        public void Permute()
        {
            int nCount = m_aPseudoColorList.Count;
            Random random = new Random(DateTime.Now.Millisecond);
            int nRandSource = random.Next(0, nCount);
            int nRandDest = random.Next(0, nCount);

            Color colorSource = Color.Transparent;
            Color colorDest = Color.Transparent;

            int i = 0;
            foreach (KeyValuePair<Color, Color> keyValuePair in m_aPseudoColorList)
            {
                if (i == nRandSource)
                {
                    colorSource = keyValuePair.Value;
                    break;
                }
            }
            foreach (KeyValuePair<Color, Color> keyValuePair in m_aPseudoColorList)
            {
                if (i == nRandDest)
                {
                    colorDest = keyValuePair.Value;
                    break;
                }
            }
            Swap(colorSource, colorDest);
        }

        /// <summary>
        /// Swaps Color source with Color Dest
        /// </summary>
        /// <param name="_colorSource"></param>
        /// <param name="_colorDest"></param>
        public void Swap(Color _colorSource, Color _colorDest)
        {
            Color colorTemp = m_aPseudoColorList[_colorDest];
            m_aPseudoColorList[_colorDest] = _colorSource;
            m_aPseudoColorList[_colorSource] = colorTemp;
        }

        /// <summary>
        /// Use this to interpolate steps...
        /// </summary>
        /// <param name="_aColor"></param>
        public ColorList(params Color[] _aColor)
        {
            InterpolateGrayMap(_aColor);
        }

        /// <summary>
        /// sort colors, 
        /// </summary>
        /// <param name="_aColor"></param>
        /// <param name="_bSort"></param>
        public ColorList(bool _bSort, params Color[] _aColor)
        {
            System.Array.Sort(_aColor);
        }

        /// <summary>
        /// gray returns a linear grayscale colormap.
        /// </summary>
        public static ColorList Gray
        {
            get { return new ColorList(); }
        }

        /// <summary>
        /// bone is a grayscale colormap with a higher value for the blue component.
        /// This colormap is useful for adding an "electronic" look to grayscale images.
        /// </summary>
        public static ColorList Bones
        {
            get { return new ColorList(Definitions.White, Definitions.Blue); }
        }

        /// <summary>
        /// hot varies smoothly from black through shades of red, orange, and yellow, to white.
        /// </summary>
        public static ColorList Hot
        {
            get { return new ColorList(Definitions.Black, Definitions.Red, Color.Orange, Definitions.Yellow, Definitions.White); }
        }

        /// <summary>
        /// colorcube contains as many regularly spaced colors in RGB color space as possible, 
        /// while attempting to provide more steps of gray, pure red, pure green, and pure blue.
        /// </summary>
        public static ColorList ColorCube
        {
            get { return new ColorList(); }
        }

        /// <summary>
        /// jet ranges from blue to red, and passes through the colors cyan, yellow, and orange. 
        /// It is a variation of the hsv colormap. 
        /// The jet colormap is associated with an astrophysical fluid jet simulation from the National Center for Supercomputer Applications.
        /// See Examples.
        /// </summary>
        public static ColorList Jet
        {
            get { return new ColorList(Definitions.Red, Definitions.Cyan, Definitions.Yellow, Color.Orange); }
        }

        /// <summary>
        /// The spinmap function shifts the colormap RGB values by some incremental value. 
        /// For example, if the increment equals 1, color 1 becomes color 2, color 2 becomes color 3, etc.
        /// spinmap cyclically rotates the colormap for approximately five seconds using an incremental value of 2.
        /// spinmap(t) rotates the colormap for approximately 10*t seconds. 
        /// The amount of time specified by t depends on your hardware configuration (e.g., if you are running MATLAB software over a network).
        /// </summary>
        public void SpinMap(int _nSpinDistance)
        {
        }

        /// <summary>
        /// copper varies smoothly from black to bright copper.
        /// </summary>
        public static ColorList Copper
        {
            get { return new ColorList(Definitions.Black, Color.FromArgb(218, 138, 103)); }
        }

        /// <summary>
        /// sv varies the hue component of the hue-saturation-value color model.
        /// The colors begin with red, pass through yellow, green, cyan, blue, magenta, and return to red.
        /// The colormap is particularly appropriate for displaying periodic functions. 
        /// hsv(m) is the same as hsv2rgb([h ones(m,2)]) where h is the linear ramp, h = (0:m–1)'/m.
        /// </summary>
        public static ColorList HSV
        {
            get { return new ColorList(); }
        }

        /// <summary>
        /// flag consists of the colors red, white, blue, and black.
        /// This colormap completely changes color with each index increment.
        /// </summary>
        /// <returns></returns>
        public static ColorList Flag
        {
            get { return new ColorList(Definitions.Red, Definitions.White, Definitions.Blue, Definitions.Black); }
        }

        /// <summary>
        /// autumn varies smoothly from red, through orange, to yellow.
        /// </summary>
        public static ColorList Autumn
        {
            get { return new ColorList(Definitions.Red, Color.Orange, Definitions.Yellow); }
        }

        /// <summary>
        /// summer consists of colors that are shades of green and yellow.
        /// </summary>
        public static ColorList Summer
        {
            get { return new ColorList(Definitions.Green, Definitions.Yellow); }
        }

        /// <summary>
        /// cool consists of colors that are shades of cyan and magenta.
        /// It varies smoothly from cyan to magenta.
        /// </summary>
        public static ColorList Cool
        {
            get { return new ColorList(Definitions.Cyan, Definitions.Magenta); }
        }

        /// <summary>
        /// white is an all white monochrome colormap.
        /// </summary>
        public static ColorList White
        {
            get { return new ColorList(); }
        }

        /// <summary>
        /// winter consists of colors that are shades of blue and green.
        /// </summary>
        public static ColorList Winter
        {
            get { return new ColorList(Definitions.Blue, Definitions.Green); }
        }

        /// <summary>
        /// lines produces a colormap of colors specified by the axes ColorOrder property and a shade of gray.
        /// </summary>
        public static ColorList Lines
        {
            get { return new ColorList(); }
        }

        /// <summary>
        /// prism repeats the six colors red, orange, yellow, green, blue, and violet.
        /// </summary>
        public static ColorList Prims
        {
            get { return new ColorList(Definitions.Red, Color.Orange, Definitions.Yellow, Definitions.Green, Definitions.Blue, Color.Violet); }
        }

        /// <summary>
        /// pink contains pastel shades of pink. 
        /// The pink colormap provides sepia tone colorization of grayscale photographs.
        /// </summary>
        public static ColorList Pink
        {
            get { return new ColorList(Color.Pink, Color.LightGray); }
        }

        /// <summary>
        /// RALPalette
        /// </summary>
        public static List<Color> RALPalette
        {
            get
            {
                Dictionary<Color, RALIndex> aRAL = RAL.CreateRalPaletteDictionary();
                List<Color> aColor = new List<Color>();
                foreach (KeyValuePair<Color, RALIndex> keyValuePair in aRAL)
                {
                    aColor.Add(keyValuePair.Key);
                }
                return aColor;
            }
        }

        /// <summary>
        /// This will interpolate colors.
        /// All given colors are separated into equi-distant interpolations.
        /// e.g. if n colors are given
        /// 255/n steps will be calculated for each color...
        /// </summary>
        /// <param name="_aColor"></param>
        protected void InterpolateGrayMap(IEnumerable<Color> _aColor)
        {
            m_aPseudoColorList.Clear();
            int nCount = _aColor.Count<Color>();
            int nSteps = 256 / (nCount - 1) - 1;

            Color lastColor = Color.Transparent;
            int nGray = 0;

            float fRFactor = 0.0f;
            float fGFactor = 0.0f;
            float fBFactor = 0.0f;

            Color color = Color.Transparent;
            float nR = 0;
            float nG = 0;
            float nB = 0;

            Color colorNext = Color.Transparent;
            for (int i = 0; i < nCount - 1; i++)
            {
                lastColor = _aColor.ElementAt<Color>(i);
                colorNext = _aColor.ElementAt<Color>(i + 1);

                fRFactor = (colorNext.R - lastColor.R) / nSteps;
                fGFactor = (colorNext.G - lastColor.G) / nSteps;
                fBFactor = (colorNext.B - lastColor.B) / nSteps;

                nR = lastColor.R;
                nG = lastColor.B;
                nB = lastColor.G;

                for (int j = 0; j < nSteps; j++)
                {
                    CalcColors(ref nGray, fRFactor, fGFactor, fBFactor, ref color, ref nR, ref nG, ref nB);
                }

                //odd steps
                if (nSteps / 2 != nSteps % 2)
                {
                    CalcColors(ref nGray, fRFactor, fGFactor, fBFactor, ref color, ref nR, ref nG, ref nB);
                }
            }

        }

        private static void CalcColors(ref int nGray, float fRFactor, float fGFactor, float fBFactor, ref Color color, ref float nR, ref float nG, ref float nB)
        {
            nR = ImageMath.Clamp(nR, 0, 255);
            nG = ImageMath.Clamp(nG, 0, 255);
            nB = ImageMath.Clamp(nB, 0, 255);

            color = Color.FromArgb((int)nR, (int)nG, (int)nB);

            Color grayColor = Color.FromArgb(nGray, nGray, nGray);
            if (!m_aPseudoColorList.ContainsKey(grayColor)) m_aPseudoColorList.Add(grayColor, color);
            nR += fRFactor;
            nG += fGFactor;
            nB += fBFactor;

            nGray++;
        }

        /// <summary>
        /// Gets or sets PseudoColorList
        /// </summary>
        public Dictionary<Color, Color> PseudoColorList
        {
            get { return m_aPseudoColorList; }
            set { m_aPseudoColorList = value; }
        }

        /// <summary>
        /// Gets or sets a color
        /// </summary>
        /// <param name="_color"></param>
        /// <returns></returns>
        public Color this[Color _color]
        {
            get { return m_aPseudoColorList[_color]; }
            set { m_aPseudoColorList[_color] = value; }

        }
    }
}
